﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Globalization;
using System.Windows.Input;
using System.Windows.Media.Imaging;

namespace Utility
{
    public enum CalibStage
    {
        Init = 0,
        High = 1,
        Low = 2,
    }

    public class CustomCanvas : Canvas
    {

        ImageBrush imgBrush = new ImageBrush();
        static bool bInit = false;

        public void UpdateBackGroupImage(string sFile)
        {
            imgBrush.ImageSource = new BitmapImage(new Uri(sFile));
            imgBrush.ViewportUnits = BrushMappingMode.RelativeToBoundingBox;
            imgBrush.Viewbox = new Rect(0, 0.1, 1, 0.4);
            Background = imgBrush;
        }
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            if (!bInit)
            {
                bInit = true;
                UpdateBackGroupImage("d:\\test.jpg");
            }

            //drawingContext.DrawImage(img, new Rect(0, 0, img.PixelWidth, img.PixelHeight));
            if (this.Children.Count == 0)
                return;

            MyVisualHost visualHost = (MyVisualHost)this.Children[0];
            if (visualHost == null)
                return;

            base.OnRender(drawingContext);
            if (visualHost == null)
                return;

            if (visualHost.Visuals == null)
                return;

            List<Point> ptsROI = new List<Point>();
            List<Point> ptsMark = new List<Point>();
            foreach (MyDrawingVisual visual in visualHost.Visuals)
            {
                Point pt = new Point(visual.StartPos.X + visual.Offset.X, visual.StartPos.Y + visual.Offset.Y);
                if (visual.Name.IndexOf("ROI") != -1)
                    ptsROI.Add(pt);

                if (visual.Name.IndexOf("Marker") != -1)
                    ptsMark.Add(pt);
            }
            List<Point> pts2Draw = new List<Point>();
            List<double> lengths = new List<double>();
            for (int i = 0; i < ptsROI.Count; i += 2)
            {
                pts2Draw.Add(ptsROI[i]);
                pts2Draw.Add(new Point(ptsROI[i + 1].X, ptsROI[i].Y));
                pts2Draw.Add(ptsROI[i + 1]);
                pts2Draw.Add(new Point(ptsROI[i].X, ptsROI[i + 1].Y));
                lengths.Add(10 + Math.Abs(ptsROI[i].X - ptsROI[i + 1].X));
            }
            for (int g = 0; g < pts2Draw.Count; g += 4)
            {
                for (int i = 0; i < 4; i++)
                {
                    Point ptStart = pts2Draw[g + i % 4];
                    Point ptEnd = pts2Draw[g + (i + 1) % 4];
                    drawingContext.DrawLine(new Pen(Brushes.Blue, 1), ptStart, ptEnd);
                }
            }

            //draw markers
            for (int i = 0; i < ptsMark.Count; i++)
            {
                drawingContext.DrawLine(new Pen(Brushes.Red, 1), ptsMark[i], new Point(ptsMark[i].X + lengths[i / 2], ptsMark[i].Y + 0));
            }

            //draw stage indication
            if (visualHost.Stage == CalibStage.Init)
                return;

            string sStageDescription = "步骤1：校准高液面";
            if (visualHost.Stage == CalibStage.Low)
            {
                sStageDescription = "步骤2：校准低液面";
            }
            FormattedText text = new FormattedText(sStageDescription,
            CultureInfo.CurrentCulture,
            FlowDirection.LeftToRight,
            new Typeface("SimSun"),
            32,
            Brushes.Red);
            drawingContext.DrawText(text, new Point(200, 100));
        }
    }

    public class MyDrawingVisual : DrawingVisual
    {
        public string Name { get; set; }
        public Point StartPos { get; set; }

    }

    public class MyVisualHost : FrameworkElement
    {
        // Create a collection of child visual objects.
        private VisualCollection _children;
        MyDrawingVisual selectedVisual;
        Point ptStart;
        Dictionary<string, Point> initPositions = new Dictionary<string, Point>();
        //Dictionary<string, Point> markers = new Dictionary<string, Point>();
        public delegate void PositionChanged();
        public event PositionChanged onPositionChanged;
        bool m_Runtime = false;
        public VisualCollection Visuals
        {
            get
            {
                return _children;
            }
        }

        public void SetMarkerPositions( List<MARKER_POS> markerPositions)
        {
            foreach (MyDrawingVisual drawingVisual in Visuals)
            {
                if (!drawingVisual.Name.Contains(stringRes.Marker))
                    continue;

                string sIndex = GetNumber(drawingVisual.Name);
                int index = int.Parse(sIndex);
                if (index <= markerPositions.Count)
                {
                    double ratio = Calib.ratio;
                    double baseHeight = Calib.Instance.roiList[index - 1].ptStart.Y;
                    double height = markerPositions[index - 1].high * ratio;
                    if (drawingVisual.Name.Contains(stringRes.LowMarker)) //high marker
                    {
                        height = markerPositions[index - 1].low * ratio;
                    }
                    height += baseHeight;
                    drawingVisual.Offset = new Vector(drawingVisual.Offset.X, height - initPositions[drawingVisual.Name].Y);
                }
            }
        }

        public MyVisualHost(bool bRuntime = false, int samples2Show = 10)
        {
            m_Runtime = bRuntime;
            _children = new VisualCollection(this);
            List<ROI> roi_s = Calib.Instance.roiList;
            List<MARKER_POS> calibPositions = Calib.Instance.calibPositions;
            List<Point> markPts = new List<Point>();
            int index = 0;
            foreach (ROI roi in roi_s)
            {
                index++;
                if (index > samples2Show)
                    break;
                initPositions.Add(string.Format("ROI_TopLeft{0}", index), roi.ptStart);
                initPositions.Add(string.Format("ROI_RightBottom{0}", index),roi.ptEnd);
                initPositions.Add(string.Format(stringRes.HighMarker + "{0}", index), new Point(roi.ptStart.X - 10, calibPositions[index - 1].high));
                initPositions.Add(string.Format(stringRes.LowMarker + "{0}", index), new Point(roi.ptStart.X - 10, calibPositions[index - 1].low));
            }
            foreach (KeyValuePair<string, Point> ptInfo in initPositions)
            {
                _children.Add(CreateDrawVisual(ptInfo));
            }
            this.MouseMove += MyVisualHost_MouseMove;
            this.MouseLeftButtonDown += MyVisualHost_MouseLeftButtonDown;
            this.MouseLeftButtonUp += new MouseButtonEventHandler(MyVisualHost_MouseLeftButtonUp);
        }

        private Visual CreateDrawVisual(KeyValuePair<string, Point> ptInfo)
        {
            if (ptInfo.Key.IndexOf(stringRes.Marker) != -1)
            {
                bool bHigh = ptInfo.Key.IndexOf(stringRes.HighMarker) != -1;
                return CreateDrawingVisualMarker(ptInfo, bHigh);
            }
            else
                return CreateDrawingVisualROIs(ptInfo);
        }


        public List<ROI> GetVisualROIs(string visualName)
        {
            List<ROI> rois = new List<ROI>();
            List<Point> ptsROI = new List<Point>();
            foreach (MyDrawingVisual visual in Visuals)
            {
                Point pt = new Point(visual.StartPos.X + visual.Offset.X, visual.StartPos.Y + visual.Offset.Y);
                if (visual.Name.IndexOf(visualName) != -1)
                {
                    ptsROI.Add(pt);
                }
            }
            for (int i = 0; i < ptsROI.Count; i += 2)
            {
                ROI roi = new ROI(ptsROI[i], ptsROI[i + 1]);
                rois.Add(roi);
            }
            return rois;
        }

        public List<double> GetVisualHeights(string visualName)
        {
            List<double> vals = new List<double>();
            foreach (MyDrawingVisual visual in Visuals)
            {
                Point pt = new Point(visual.StartPos.X + visual.Offset.X, visual.StartPos.Y + visual.Offset.Y);
                if (visual.Name.IndexOf(visualName) != -1)
                    vals.Add(pt.Y);
            }
            return vals;
        }



        private MyDrawingVisual CreateDrawingVisualROIs(KeyValuePair<string, Point> ptInfo)
        {
            MyDrawingVisual drawingVisual = new MyDrawingVisual();
            drawingVisual.StartPos = ptInfo.Value;
            DrawingContext drawingContext = drawingVisual.RenderOpen();
            drawingVisual.Name = ptInfo.Key;
            Point pos = ptInfo.Value;
            int sz = 10;
            Point ptUp = new Point(pos.X, pos.Y - sz);
            Point ptRight = new Point(pos.X + sz, pos.Y);
            Point ptDown = new Point(pos.X, pos.Y + sz);
            Point ptLeft = new Point(pos.X - sz, pos.Y);
            string s = string.Format("M {0},{1} L {2},{3} {4},{5} {6},{7} {8},{9}",
                            ptUp.X, ptUp.Y, ptLeft.X, ptLeft.Y,
                            ptDown.X, ptDown.Y, ptRight.X, ptRight.Y, ptUp.X, ptUp.Y);

            int index = int.Parse(GetNumber(ptInfo.Key));
            FormattedText text = new FormattedText(index.ToString(),
            CultureInfo.CurrentCulture,
            FlowDirection.LeftToRight,
            new Typeface("SimSun"),
            16,
            Brushes.Black);

            Geometry geometry = Geometry.Parse(s);
            if (drawingVisual.Name.IndexOf("TopLeft") != -1)
                drawingContext.DrawText(text, new Point(pos.X + 10, pos.Y));
            if (!m_Runtime)
                drawingContext.DrawGeometry(Brushes.LightGreen, new Pen(Brushes.Red, 2), geometry);
            drawingContext.Close();

            return drawingVisual;
        }



        private MyDrawingVisual CreateDrawingVisualMarker(KeyValuePair<string, Point> ptInfo, bool bHigh)
        {
            MyDrawingVisual drawingVisual = new MyDrawingVisual();
            DrawingContext drawingContext = drawingVisual.RenderOpen();
            drawingVisual.StartPos = ptInfo.Value;
            drawingVisual.Name = ptInfo.Key;
            Point pos = ptInfo.Value;
            Point ptUp = new Point(pos.X - 10, pos.Y - 10);
            Point ptDown = new Point(pos.X - 10, pos.Y + 10);
            Point ptRight = new Point(pos.X + 10, pos.Y);
            string s = string.Format("M {0},{1} {2},{3} {4},{5} {0},{1}", ptUp.X, ptUp.Y, ptDown.X, ptDown.Y, ptRight.X, ptRight.Y);
            Geometry geometry = Geometry.Parse(s);
            Brush brush = bHigh ? Brushes.Red : Brushes.SeaGreen;
            drawingContext.DrawGeometry(brush, new Pen(Brushes.Yellow, 1), geometry);
            drawingContext.Close();
            return drawingVisual;

        }

        string GetNumber(String str)
        {
            string returnStr = string.Empty;
            for (int i = 0; i < str.Length; i++)
            {
                if (Char.IsNumber(str, i))
                {
                    returnStr += str.Substring(i, 1);
                }
            }
            return returnStr;
        }

        void MyVisualHost_MouseMove(object sender, MouseEventArgs e)
        {
            if (selectedVisual == null)
                return;

            if (selectedVisual.Name.IndexOf(stringRes.Marker) != -1)
            {
                selectedVisual.Offset = new Vector(selectedVisual.Offset.X, (e.GetPosition(this) - initPositions[selectedVisual.Name]).Y);
            }
            else
            {
                selectedVisual.Offset = (e.GetPosition(this) - initPositions[selectedVisual.Name]);

                string sIndex = GetNumber(selectedVisual.Name);
                string sCorrespondingMark = stringRes.Marker + sIndex.ToString();
                if (selectedVisual.Name.IndexOf("TopLeft") != -1)
                {
                    foreach (MyDrawingVisual tempVisual in _children)
                    {
                        string sTmpIndex = GetNumber(tempVisual.Name);
                        if (sTmpIndex == sIndex && tempVisual.Name.IndexOf(stringRes.Marker) != -1)
                        {

                            tempVisual.Offset = new Vector(selectedVisual.Offset.X, tempVisual.Offset.Y);
                        }
                    }
                }
            }
            if (onPositionChanged != null)
                onPositionChanged();
        }

        void MyVisualHost_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            System.Windows.Point pt = e.GetPosition((UIElement)sender);

            // Initiate the hit test by setting up a hit test result callback method.
            VisualTreeHelper.HitTest(this, null, new HitTestResultCallback(myCallback), new PointHitTestParameters(pt));

            if (selectedVisual != null)
                ptStart = e.GetPosition(this);
        }

        // Capture the mouse event and hit test the coordinate point value against
        // the child visual objects.
        void MyVisualHost_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            // Retreive the coordinates of the mouse button event.
            if (selectedVisual != null)
                selectedVisual.Opacity = 1.0;

            selectedVisual = null;
        }

        // If a child visual object is hit, toggle its opacity to visually indicate a hit.
        public HitTestResultBehavior myCallback(HitTestResult result)
        {
            selectedVisual = null;
            if (result.VisualHit.GetType() == typeof(MyDrawingVisual))
            {
                MyDrawingVisual hitViusal = (MyDrawingVisual)(result.VisualHit);
                if (Stage == CalibStage.Low && hitViusal.Name.IndexOf(stringRes.HighMarker) != -1)
                    return HitTestResultBehavior.Stop;

                if (Stage == CalibStage.High && hitViusal.Name.IndexOf(stringRes.LowMarker) != -1)
                    return HitTestResultBehavior.Stop;

                if (Stage == CalibStage.Init && hitViusal.Name.IndexOf("ROI") != -1)
                    return HitTestResultBehavior.Stop;
                ((DrawingVisual)result.VisualHit).Opacity = 0.4;
                selectedVisual = (MyDrawingVisual)result.VisualHit;
            }
            // Stop the hit test enumeration of objects in the visual tree.
            return HitTestResultBehavior.Stop;
        }

        // Provide a required override for the VisualChildrenCount property.
        protected override int VisualChildrenCount
        {
            get { return _children.Count; }
        }

        // Provide a required override for the GetVisualChild method.
        protected override Visual GetVisualChild(int index)
        {
            if (index < 0 || index >= _children.Count)
            {
                throw new ArgumentOutOfRangeException();
            }

            return _children[index];
        }

        public CalibStage Stage { get; set; }
    }
}
